home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2000 #5 / Amiga Plus CD - 2000 - No. 5.iso / Tools / Musik / Misc / Amster / Source / download.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-01  |  20.8 KB  |  846 lines

  1. /*
  2. ** Download
  3. */
  4.  
  5. #include "include/config.h"
  6.  
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10.  
  11. #include <proto/exec.h>
  12. #include <proto/socket.h>
  13. #include <proto/dos.h>
  14. #include <proto/asl.h>
  15. #include <exec/memory.h>
  16. #include <libraries/dos.h>
  17. #include <libraries/asl.h>
  18. #include <netdb.h>
  19. #include <sys/time.h>
  20. #include <sys/socket.h>
  21. #include <sys/ioctl.h>
  22. #include <netinet/tcp.h>
  23. #include <bsdsocket/socketbasetags.h>
  24. #include <error.h>
  25. #include <time.h>
  26.  
  27. #include "include/mui.h"
  28. #include <MUI/NListview_mcc.h>
  29. #include <MUI/NFloattext_mcc.h>
  30. #include "include/gui.h"
  31. #include "include/rexx.h"
  32. #include "include/panel.h"
  33. #include "include/transfer.h"
  34. #include "include/download.h"
  35. #include "include/prefs.h"
  36. #include "include/share.h"
  37. #include "md5.h"
  38. #include "amster_Cat.h"
  39. #include "include/protos.h"
  40.  
  41.  
  42. int dl_count = 0;
  43. int QueueCount = 0;
  44. int WaitingCount = 0;
  45.  
  46. /* Private functions */
  47.  
  48. ULONG dl_new(struct IClass *cl, Object *obj, struct opSet *msg);
  49. void dl_startq(struct TransferData *data, Object *obj, char *title, char *user, u_long ip, int port);
  50. void dl_checkqueue(struct TransferData *data);
  51. void DownloadRetry(struct TransferData *data, Object *obj, char *title, char *user, int limit);
  52. void PollWaiting(struct TransferData *data, Object *obj);
  53. int dl_filename(songtrans sd);
  54. char *chkmd5_fromlock(BPTR lock);
  55. int dl_askfname(char *fname);
  56. void dl_handlemsg(thread t, int com, APTR data);
  57. void dl_resume(struct TransferData *data);
  58. void dl_cps(struct TransferData *data);
  59. __asm __saveds void dl_sucker(void);
  60.  
  61.  
  62. MUIF dl_dispatch(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg)
  63. {
  64.     struct TransferData *data;
  65.  
  66.     switch(msg->MethodID) {
  67.         case OM_NEW:
  68.             return(dl_new(cl, obj, (APTR)msg));
  69.         case MUIM_Window_Setup:
  70.             return(dl_setup(cl, obj, (APTR)msg));
  71.         case MUIM_Window_Cleanup:
  72.             return(dl_muicleanup(cl, obj, (APTR)msg));
  73.         case DL_ADD:
  74.             data = INST_DATA(cl, obj);
  75.             DoMethod(data->list, MUIM_NList_InsertSingle, (songtrans)(((muimsg)msg)->arg1), MUIV_NList_Insert_Bottom);
  76.             return(NULL);
  77.         case DL_START:
  78.             data = INST_DATA(cl, obj);
  79.             dl_startq(data, obj, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2), (u_long)(((muimsg)msg)->arg3), (int)(((muimsg)msg)->arg4));
  80.             return(NULL);
  81.         case DL_UPDATE:
  82.             {
  83.             long pos = MUIV_NList_GetPos_Start;
  84.             data = INST_DATA(cl, obj);
  85.             DoMethod(data->list, MUIM_NList_GetPos, (((muimsg)msg)->arg1), &pos);
  86.             DoMethod(data->list, MUIM_NList_Redraw, pos);
  87.             return(NULL);
  88.             }
  89.         case DL_CPS:
  90.             data = INST_DATA(cl, obj);
  91.             dl_cps(data);
  92.             return(NULL);
  93.         case DL_CLEANUP:
  94.             data = INST_DATA(cl, obj);
  95.             TransferCleanup(data);
  96.             return(NULL);
  97.         case DL_ABORT:
  98.             data = INST_DATA(cl, obj);
  99.             TransferAbort(data);
  100.             return(NULL);
  101.         case DL_RESUME:
  102.             data = INST_DATA(cl, obj);
  103.             dl_resume(data);
  104.             return(NULL);
  105.         case DL_INFO:
  106.             data = INST_DATA(cl, obj);
  107.             TransferInfo(data);
  108.             return(NULL);
  109.         case DL_PLAY:
  110.             {
  111.             u_long palpa;
  112.             char buf[1024], *command;
  113.             data = INST_DATA(cl, obj);
  114.  
  115.             DoMethod(data->list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &palpa);
  116.             if (!palpa) return(NULL);
  117.             if (((songtrans)palpa)->state < DLS_DOWN) return(NULL);
  118.             if (prf->scripts[PRFE_PLAYMP3]) {
  119.                 sprintf(buf, "Run <>NIL: %s", prf->scripts[PRFE_PLAYMP3]);
  120.                 command = strrep(buf, "%f", ((songtrans)palpa)->fname);
  121.                 Execute(command, 0, 0);
  122.                 free(command);
  123.             }
  124.             return(NULL);
  125.             }
  126.         case DL_SETERROR:
  127.             data = INST_DATA(cl, obj);
  128.             TransferSetError(data, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2), (int)(((muimsg)msg)->arg3));
  129.             return(NULL);
  130.         case DL_RETRY:
  131.             data = INST_DATA(cl, obj);
  132.             DownloadRetry(data, obj, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2), (int)(((muimsg)msg)->arg3));
  133.             return(NULL);
  134.         case DL_POLLWAIT:
  135.             data = INST_DATA(cl, obj);
  136.             PollWaiting(data, obj);
  137.             return(NULL);
  138.         case DL_REMWAITING:
  139.             data = INST_DATA(cl, obj);
  140.             if (((songtrans)(((muimsg)msg)->arg1))->state == DLS_WAIT) {
  141.                 if (--WaitingCount == 0) DoMethod(_app(obj), MUIM_Application_RemInputHandler, &data->waitnode);
  142.                 /* This was the last waiting entry - remove input handler */
  143.             }
  144.             return(NULL);
  145.         case DL_SETDELAY:
  146.             data = INST_DATA(cl, obj);
  147.             data->waitnode.ihn_Millis = (int)(((muimsg)msg)->arg1)*1000;
  148.             return(NULL);
  149.         case DL_WATCHER:
  150.             data = INST_DATA(cl, obj);
  151.             TransferWatcher(data);
  152.             return(NULL);
  153.         case DL_COUNTDECREMENT:
  154.             data = INST_DATA(cl, obj);
  155.             if (--dl_count == 0) DoMethod(_app(obj), MUIM_Application_RemInputHandler, &data->watchnode);
  156.             dl_checkqueue(data);
  157.             return(NULL);
  158.         case DL_COUNTINCREMENT:
  159.             data = INST_DATA(cl, obj);
  160.             if (dl_count++ == 0) DoMethod(_app(obj), MUIM_Application_AddInputHandler, &data->watchnode);
  161.             return(NULL);
  162.         case DL_CHECKQUEUE:
  163.             data = INST_DATA(cl, obj);
  164.             dl_checkqueue(data);
  165.             return(NULL);
  166.     }
  167.     return(DoSuperMethodA(cl, obj, msg));
  168. }
  169.  
  170.  
  171. ULONG dl_new(struct IClass *cl, Object *obj, struct opSet *msg)
  172. {
  173.     static struct Hook downlistdispHook = { {0,0}, &translistdisp, NULL, NULL };
  174.     struct TransferData *data;
  175.     Object *list, *info, *playbut, *abortbut, *resbut, *cleanbut;
  176.  
  177.     if (obj = (Object *)DoSuperNew(cl, obj,
  178.         MUIA_HelpNode, "download",
  179.         MUIA_Window_Title, MSG_DL_TITLE,
  180.         MUIA_Window_ID, MAKE_ID('D','O','W','N'),
  181.         WindowContents, VGroup,
  182.             Child, list = NListviewObject,
  183.                 MUIA_NListview_NList, NListObject,
  184.                     InputListFrame,
  185.                     MUIA_Font, MUIV_Font_Tiny,
  186.                     MUIA_NList_Title, TRUE,
  187.                     MUIA_NList_Format, "BAR, BAR, BAR, BAR, BAR",
  188.                     MUIA_NList_DisplayHook, &downlistdispHook,
  189.                     MUIA_NList_DragSortable, TRUE,
  190.                     MUIA_CycleChain, 1,
  191.                 End,
  192.             End,
  193.             Child, info = TextObject,
  194.                 TextFrame,
  195.                 MUIA_Background, MUII_TextBack,
  196.                 MUIA_Text_PreParse, "\33c",
  197.             End,
  198.             Child, HGroup,
  199.                 Child, HGroup,
  200.                     Child, playbut = SimpleButton(MSG_DL_PLAY_GAD),
  201.                     MUIA_ShortHelp, MSG_PLAY_HELP,
  202.                 End,
  203.                 Child, HSpace(4),
  204.                 Child, HGroup,
  205.                     Child, abortbut = SimpleButton(MSG_DL_ABORT_GAD),
  206.                     MUIA_ShortHelp, MSG_ABORT_HELP,
  207.                 End,
  208.                 Child, HGroup,
  209.                     Child, resbut = SimpleButton(MSG_DL_RESUME_GAD),
  210.                     MUIA_ShortHelp, MSG_RESUME_HELP,
  211.                 End,
  212.                 Child, HSpace(4),
  213.                 Child, HGroup,
  214.                     Child, cleanbut = SimpleButton(MSG_DL_CLEANUP_GAD),
  215.                     MUIA_ShortHelp, MSG_CLEANUP_HELP,
  216.                 End,
  217.             End,
  218.         End,
  219.         TAG_MORE, msg->ops_AttrList))
  220.     {
  221.         data = INST_DATA(cl,obj);
  222.         data->list = list;
  223.         data->info = info;
  224.  
  225.         data->ihnode.ihn_Object = obj;
  226.         data->ihnode.ihn_Millis = 1000;
  227.         data->ihnode.ihn_Method = DL_CPS;
  228.         data->ihnode.ihn_Flags  = MUIIHNF_TIMER;
  229.  
  230.         data->waitnode.ihn_Object = obj;
  231.         data->waitnode.ihn_Millis = prf->QueueDelay*1000;    /* Poll interval in milliseconds */
  232.         data->waitnode.ihn_Method = DL_POLLWAIT;
  233.         data->waitnode.ihn_Flags  = MUIIHNF_TIMER;
  234.  
  235.         data->watchnode.ihn_Object = obj;
  236.         data->watchnode.ihn_Millis = 10000;
  237.         data->watchnode.ihn_Method = DL_WATCHER;
  238.         data->watchnode.ihn_Flags  = MUIIHNF_TIMER;
  239.  
  240.         DoMethod(playbut,  MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, DL_PLAY   );
  241.         DoMethod(resbut,   MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, DL_RESUME );
  242.         DoMethod(abortbut, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, DL_ABORT  );
  243.         DoMethod(cleanbut, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, DL_CLEANUP);
  244.  
  245.         DoMethod(list, MUIM_Notify, MUIA_NList_EntryClick, MUIV_EveryTime, obj, 1, DL_INFO);
  246.         DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, gui->iconpanel, 1, PANEL_CLOSEDL);
  247.  
  248.         return((ULONG)obj);
  249.     }
  250.  
  251.     return(0);
  252. }
  253.  
  254.  
  255. void dl_addq(song s)
  256. /* This is the initial function, called from resultview.c */
  257. {
  258.     songtrans sd;
  259.  
  260.     sd = malloc(sizeof(_songtrans));
  261.     if (!sd) return;
  262.     memset(sd, 0, sizeof(_songtrans));
  263.  
  264.     sd->song = nap_songdup(s);
  265.     if (!sd->song) {
  266.         free(sd);
  267.         return;
  268.     }
  269.  
  270.     sd->size = s->size;
  271.     sd->mynick = prf->user;
  272.     sd->type = TYPE_DOWNLOAD_OUT;
  273.     sd->reqtime = time(NULL);
  274.  
  275.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) {
  276.         sd->state = DLS_QUEUE;
  277.         QueueCount++;
  278.     }
  279.     else {
  280.         sprintf(nap_buf, "\"%s\" \"%s\"", s->user, s->title);
  281.         nap_send(NAPC_FILEINFOREQ);
  282.         sd->state = DLS_PREP;
  283.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  284.     }
  285.  
  286.     DoMethod(gui->dwin, DL_ADD, sd);
  287. }
  288.  
  289.  
  290. void dl_startq(struct TransferData *data, Object *obj, char *title, char *user, u_long ip, int port)
  291. /* This is the initial function, when the actual download is about to
  292.    take place (acknowledged by server) - called from napster.c (via DL_START) */
  293. {
  294.     u_long tmp;
  295.     songtrans sd;
  296.     long i;
  297.  
  298.     for (i=0; ; i++) {
  299.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  300.         if (!tmp) return;
  301.         sd = (songtrans)tmp;
  302.         if ((sd->state == DLS_PREP || sd->state == DLS_WAIT || sd->state == DLS_QUEUE) && stricmp(user,sd->song->user)==0 && strcmp(title,sd->song->title)==0) break;
  303.     }
  304.  
  305.     if (sd->t) {
  306. gui_debug("this shouldn't happen!");
  307.         return;    /* Already in a thread */
  308.     }
  309.  
  310.     if (!dl_filename(sd)) {
  311.         if (sd->state == DLS_WAIT) DoMethod(gui->dwin, DL_REMWAITING, sd);
  312.         else if (sd->state == DLS_QUEUE) QueueCount--;
  313.         else {
  314.             sd->state = DLS_ABORT;
  315.             DoMethod(gui->dwin, DL_COUNTDECREMENT);
  316.         }
  317.         sd->state = DLS_ABORT;
  318.         DoMethod(gui->dwin, DL_UPDATE, sd);
  319.         return;
  320.     }
  321.  
  322.     sd->song->ip = ip;    /* In case of browse result, the IP isn't already there */
  323.  
  324.     sd->ip = ip;
  325.     sd->port = port;
  326.     sd->s = -1;
  327.     sd->oldsize = sd->cur;
  328.  
  329.     if (sd->state == DLS_WAIT) DoMethod(gui->dwin, DL_REMWAITING, sd);
  330.  
  331.     if (sd->state == DLS_QUEUE) {
  332.         QueueCount--;
  333.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  334.     }
  335.     else if (sd->state == DLS_WAIT) {
  336.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  337.     }
  338.  
  339.     sd->state = DLS_PREP;
  340.  
  341.     sd->t = th_spawn(dl_handlemsg, "Amster downloader", dl_sucker, prf->DownloadTaskPri, sd);
  342.     if (!sd->t) {
  343.         sd->state = DLS_ERROR;
  344.         DoMethod(gui->dwin, DL_UPDATE, sd);
  345.     }
  346. }
  347.  
  348.  
  349. void dl_checkqueue(struct TransferData *data)
  350. {
  351.     u_long tmp;
  352.     songtrans sd;
  353.     long i;
  354.  
  355.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) return;
  356.  
  357.     for (i=0; ; i++) {
  358.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  359.         if (!tmp) return;
  360.         sd = (songtrans)tmp;
  361.         if (sd->state == DLS_QUEUE) break;
  362.     }
  363.  
  364.     sd->reqtime = time(NULL);    /* Reset time to avoid watcher timeouts */
  365.  
  366.     sprintf(nap_buf, "\"%s\" \"%s\"", sd->song->user, sd->song->title);
  367.     nap_send(NAPC_FILEINFOREQ);
  368.     DoMethod(gui->dwin, DL_COUNTINCREMENT);
  369.     sd->state = DLS_PREP;
  370.     QueueCount--;
  371.  
  372.     DoMethod(gui->dwin, DL_UPDATE, sd);
  373. }
  374.  
  375.  
  376. void DownloadRetry(struct TransferData *data, Object *obj, char *title, char *user, int limit)
  377. /* Called from napster.c when a file cannot be downloaded yet (busy) */
  378. {
  379.     u_long tmp;
  380.     songtrans sd;
  381.     long i;
  382.  
  383.     for (i=0; ; i++) {
  384.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  385.         if (!tmp) return;
  386.         sd = (songtrans)tmp;
  387.         if (sd->state == DLS_PREP && strcmp(sd->song->title, title) == 0 && stricmp(sd->song->user, user) == 0) break;
  388.     }
  389.  
  390.     if (sd->RetryCount >= prf->QueueRetries) return;    /* We've already given up */
  391.     else if (limit == 0) {
  392.         sd->state = DLS_ERROR;
  393.         sd->error = ERROR_TEASER;    /* Queue limit is 0 = file can't be downloaded by anyone */
  394.  
  395.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  396.     }
  397.     else {
  398.         sd->state = DLS_WAIT;
  399.         sd->ErrorCode = limit;
  400.         WaitingCount++;
  401.  
  402.         if (WaitingCount == 1) DoMethod(_app(obj), MUIM_Application_AddInputHandler, &data->waitnode);
  403.         /* This is the first waiting entry - add input handler */
  404.  
  405.         DoMethod(gui->dwin, DL_COUNTDECREMENT);
  406.         /* We consider this queued and let other downloads start */
  407.     }
  408.  
  409.     DoMethod(gui->dwin, DL_UPDATE, sd);
  410. }
  411.  
  412.  
  413. void PollWaiting(struct TransferData *data, Object *obj)
  414. {
  415.     u_long tmp;
  416.     songtrans sd;
  417.     long i;
  418.  
  419.     if (WaitingCount == 0) return;    /* Don't waste time! */
  420.  
  421.     for (i=0; ; i++) {
  422.         DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp);
  423.         if (!tmp) return;
  424.         sd = (songtrans)tmp;
  425.         if (sd->state == DLS_WAIT && QueueCount == 0) {
  426.             if (sd->RetryCount < prf->QueueRetries) {
  427.                 sprintf(nap_buf,"\"%s\" \"%s\"", sd->song->user, sd->song->title);
  428.                 nap_send(NAPC_FILEINFOREQ);
  429.                 sd->RetryCount++;
  430.             }
  431.             else {    /* We give up! */
  432.                 sd->state = DLS_ERROR;
  433.                 sd->error = ERROR_BUSY;
  434.                 DoMethod(gui->dwin, DL_REMWAITING, sd);
  435.                 DoMethod(gui->dwin, DL_UPDATE, sd);
  436.             }
  437.         }
  438.     }
  439. }
  440.  
  441.  
  442. void dl_resume(struct TransferData *data)
  443. {
  444.     u_long item;
  445.     songtrans sd;
  446.  
  447.     DoMethod(data->list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &item);
  448.     if (!item) return;
  449.  
  450.     sd = (songtrans)item;
  451.     if (sd->state < DLS_ABORT) return;    /* Can't resume files that's already in progress */
  452.     if (sd->t) return;
  453.  
  454.     if (dl_count >= prf->DownloadQueueLimit && prf->DownloadQueueLimit < 26) {
  455.         sd->state = DLS_QUEUE;
  456.         QueueCount++;
  457.     }
  458.     else {
  459.         sd->reqtime = time(NULL);
  460.         sprintf(nap_buf, "\"%s\" \"%s\"", sd->song->user, sd->song->title);
  461.         nap_send(NAPC_FILEINFOREQ);
  462.         sd->state = DLS_PREP;
  463.         DoMethod(gui->dwin, DL_COUNTINCREMENT);
  464.     }
  465.  
  466.     DoMethod(gui->dwin, DL_UPDATE, sd);
  467. }
  468.  
  469.  
  470. int dl_filename(songtrans sd)
  471. {
  472.     BPTR lock;
  473.     struct FileInfoBlock fib;
  474.     char md5[80]="";
  475.     char *fname;
  476.     long oldsize=0;
  477.     int way, reqrc;
  478.     char *expl, *choice;
  479.  
  480.     fname = malloc(512);
  481.     if (!fname) return(0);
  482.     strcpy(fname, prf->dlpath);
  483.     AddPart(fname, nap_strippath(sd->song->title), 511);
  484.  
  485.     if (prf->askfile) {
  486.         if (!dl_askfname(fname)) return 0;
  487.     }
  488.     else {    /* Needs optimization and elegance */
  489.         if (strlen(nap_strippath(sd->song->title)) > prf->NameLength) {
  490.             strcpy(fname+strlen(fname)-strlen(nap_strippath(sd->song->title))-4+prf->NameLength, fname+strlen(fname)-4);
  491.         }
  492.     }
  493.  
  494.     lock = Lock(fname, ACCESS_READ);
  495.     if (!lock) {
  496.         sd->fname = fname;
  497.         sd->cur = 0;
  498.         return(1);
  499.     }
  500.  
  501.     if (Examine(lock, &fib)) {
  502.         oldsize = fib.fib_Size;
  503.         sscanf(fib.fib_Comment, "md5: %[^;]", &md5);
  504.         if (md5[0] == 0 && oldsize >= 300032) strcpy(md5, chkmd5_fromlock(lock));
  505.             else UnLock(lock);
  506.     }
  507.     else UnLock(lock);
  508.  
  509.     if (sd->size <= oldsize) {
  510.         way = 1;
  511.         expl = (char *)MSG_DL_RESBIGSIZE;
  512.         choice = (char *)MSG_DL_OVERCANCEL_GAD;
  513.     }
  514.     else if (md5[0] == 0) {
  515.         way = 0;
  516.         expl = (char *)MSG_DL_NOMD5INFO;
  517.         choice = (char *)MSG_DL_RESUMECANCEL_GAD;
  518.     }
  519.     else if (strcmp(md5,sd->song->md5)!=0) {
  520.         way = 1;
  521.         expl = (char *)MSG_DL_NOMATCH;
  522.         choice = (char *)MSG_DL_OVERCANCEL_GAD;
  523.     }
  524.     else {
  525.         way = 2;
  526.         expl = (char *)MSG_DL_RESMATCH;
  527.         choice = (char *)MSG_DL_RESUMEOVER_GAD;
  528.     }
  529.  
  530.     reqrc = MUI_Request(gui->app, gui->win, 0L,
  531.                         (char *)MSG_DL_RESUME_INFO,
  532.                         choice,
  533.                         (char *)MSG_DL_RESUME_MSG,
  534.                         fname,
  535.                         oldsize,
  536.                         sd->size,
  537.                         md5[0]==0 ? (char*)MSG_DL_NOMD5 : md5,
  538.                         sd->song->md5,
  539.                         expl);
  540.  
  541.     if (reqrc == 0) return(0);
  542.  
  543.     if ((way==0 || way==2) && reqrc == 1) {
  544.         sd->fname = fname;
  545.         sd->cur = oldsize;
  546.         return(1);
  547.     }
  548.  
  549.     if (reqrc == 2) {
  550.         if (dl_askfname(fname)) {
  551.             sd->fname = fname;
  552.             sd->cur = 0;
  553.             return(1);
  554.         }
  555.         else return(0);
  556.     }
  557.  
  558.     sd->cur = 0;
  559.     sd->fname = fname;
  560.     return(1);
  561. }
  562.  
  563.  
  564. char *chkmd5_fromlock(BPTR lock)
  565. {
  566.     APTR buffer;
  567.     BPTR fh;
  568.     int len;
  569.     char md5[33] = "";
  570.  
  571.     md5_state_t state;
  572.     md5_byte_t digest[16];
  573.     int di;
  574.  
  575.     md5_init(&state);
  576.  
  577.     if (fh = OpenFromLock(lock)) {
  578.         if (buffer = AllocMem(300032, MEMF_ANY)) {
  579.  
  580.             len = Read(fh, buffer, 300032);
  581.             if (len > 0) {
  582.                 md5_append(&state, (const md5_byte_t *)buffer, len);
  583.                 md5_finish(&state, digest);
  584.                 for (di = 0; di < 16; ++di)
  585.                     sprintf(md5+di*2, "%02x", digest[di]);
  586.             }
  587.             FreeMem(buffer, 300032);
  588.         }
  589.         Close(fh);
  590.     }
  591.     else UnLock(lock);
  592.  
  593.     return md5;
  594. }
  595.  
  596.  
  597. int dl_askfname(char *fname)
  598. {
  599.     struct FileRequester *freq;
  600.     u_long win;
  601.  
  602.     freq = AllocAslRequestTags(ASL_FileRequest, TAG_DONE);
  603.     if (!freq) return(0);
  604.  
  605.     get(gui->dwin, MUIA_Window_Window, &win);
  606.     if (AslRequestTags(freq,
  607.                     ASLFR_Window, win,
  608.                     ASLFR_TitleText, MSG_DL_SELECTFILE,
  609.                     ASLFR_InitialFile, nap_strippath(fname),
  610.                     ASLFR_InitialDrawer, prf->dlpath,
  611.                     ASLFR_DoSaveMode, TRUE,
  612.                     TAG_DONE)) {
  613.         strcpy(fname, freq->fr_Drawer);
  614.         AddPart(fname, freq->fr_File, 511);
  615.         FreeAslRequest(freq);
  616.         return(1);
  617.     }
  618.     else {
  619.         FreeAslRequest(freq);
  620.         return(0);
  621.     }
  622. }
  623.  
  624.  
  625. void dl_handlemsg(thread t, int com, APTR data)
  626. /* This is the function that receives messages from the download
  627.    thread. It's called when something needs to be done in the
  628.    main thread (i.e. updating the window). */
  629. {
  630.     songtrans sd=(songtrans)t->data;
  631.  
  632.     switch(com) {
  633.         case THC_STARTUP:
  634.             sd->ts = 1;
  635.             nap_sendbuf(NAPC_DLINC, "");
  636.             break;
  637.         case THC_EXIT:
  638.             sd->error = (int)data;
  639.             TransferHandleError(sd);
  640.             sd->ts = 0;
  641.             sd->t = NULL;
  642.             DoMethod(gui->dwin, DL_COUNTDECREMENT);
  643.             nap_sendbuf(NAPC_DLCOMPLETE, "");
  644.             break;
  645.         case DLC_STATE:
  646.             sd->state = (int)data;
  647.         case DLC_UPDATE:
  648.             DoMethod(gui->dwin, DL_UPDATE, sd);
  649.             break;
  650.         case DLC_ADDSHARE:
  651.             DoMethod(gui->shwin, SHARE_ADDFILE, sd->song, sd->fname);
  652.             break;
  653.     }
  654. }
  655.  
  656.  
  657. void dl_cps(struct TransferData *data)
  658. {
  659.     songtrans sd;
  660.     u_long item;
  661.     int i;
  662.  
  663.     for (i=0; ; i++) {
  664.         DoMethod(data->list, MUIM_NList_GetEntry, i, &item);
  665.         if (!item) return;
  666.         sd = (songtrans)item;
  667.         if (sd->state == DLS_DOWN) {
  668.             CalculateCps(sd);
  669.             DoMethod(gui->dwin, DL_UPDATE, sd);
  670.         }
  671.     }
  672. }
  673.  
  674.  
  675. /* thread code */
  676.  
  677. __asm __saveds void dl_sucker(void)
  678. {
  679.     thread t;
  680.     songtrans sd;
  681.     struct Library *DosBase;
  682.     struct Library *SocketBase;
  683.     char *buffer;
  684.     long tmp;
  685.     long s;
  686.     thmsg m;
  687.     char cmnt[80];
  688.     int count;
  689.  
  690.     t = thr_init();
  691.     if (!t) return;
  692.     sd = t->data;
  693.  
  694.     if (!InitTransferThread(t, sd)) return;
  695.     sd->RetryCount = 0;
  696.     s = sd->s;
  697.     buffer = sd->buffer;
  698.     DosBase = sd->DosBase;
  699.     SocketBase = sd->SocketBase;
  700.  
  701.     while (1) {
  702.         u_long sigs;
  703.  
  704.         sigs = Wait(sd->nsigm | sd->msigm);
  705.         if (sigs&(sd->msigm)) {
  706.             m = (thmsg)GetMsg(t->port);
  707.             if (m) {
  708.                 if (m->com == THC_EXIT) {
  709.                     sd->state = DLS_ABORT;
  710.                     thr_message(t, DLC_UPDATE, 0);
  711.                     ExitTransferThread(sd, 0);
  712.                     return;
  713.                 }
  714.                 if (!m->isreply) {
  715.                     m->isreply=1;
  716.                     ReplyMsg((struct Message *)m);
  717.                 }
  718.             }
  719.         }
  720.  
  721.         if (sigs&(sd->nsigm)) {
  722.             FD_ZERO(&sd->fds);
  723.             FD_SET(s,&sd->fds);
  724.             sd->tv.tv_sec = 0;
  725.             sd->tv.tv_usec = 0;
  726.  
  727.             switch (sd->state) {
  728.                 case DLS_CON:
  729.                     if (WaitSelect(s+1,NULL,&sd->fds,NULL,&sd->tv,0) != 1) break;
  730.                     {
  731.                         sd->state = DLS_REQ;
  732.                         tmp = 0;
  733.                         IoctlSocket(s, FIONBIO, (char*)&tmp);
  734.                         /* Disable non-blocking I/O to the socket */
  735.                     }
  736.  
  737.                 case DLS_REQ:
  738.                     if (WaitSelect(s+1,&sd->fds,NULL,NULL,&sd->tv,0) != 1) break;
  739.                     {
  740.                         tmp = recv(s, buffer, 1, 0);
  741.                         if (tmp != 1) {
  742.                             sd->ErrorCode = Errno();
  743.                             ExitTransferThread(sd, ERROR_NET);
  744.                             return;
  745.                         }
  746.                         if (buffer[0] != '1') {
  747.                             ExitTransferThread(sd, 29);
  748.                             return;
  749.                         }
  750.                         thr_message(t, DLC_UPDATE, 0);
  751.                         send(s, "GET", 3, 0);
  752.                         sprintf(buffer, "%s \"%s\" %d", sd->mynick, sd->song->title, sd->cur);
  753.                         send(s, buffer, strlen(buffer), 0);
  754.  
  755.                         tmp = recv(s, buffer, 32, 0);
  756.                         if (tmp < 1) {
  757.                             sd->ErrorCode = Errno();
  758.                             ExitTransferThread(sd, ERROR_NET);
  759.                             return;
  760.                         }
  761.                         buffer[tmp] = '\0';
  762.  
  763.                         if (atoi(buffer) != sd->size) {
  764.                             if (strcmp(buffer, "FILE NOT FOUND") == 0 || strcmp(buffer, "FILE NOT SHARED") == 0) {
  765.                                 ExitTransferThread(sd, ERROR_NOTFOUND);
  766.                                 return;
  767.                             }
  768.                             else if (strcmp(buffer, "INVALID REQUEST") == 0) {
  769.                                 ExitTransferThread(sd, ERROR_INVALIDREQUEST);
  770.                                 return;
  771.                             }
  772.                             ExitTransferThread(sd, 12);
  773.                             return;
  774.                         }
  775.  
  776.                         sd->f = Open(sd->fname, MODE_READWRITE);
  777.                         if (!sd->f) {
  778.                             sd->ErrorCode = IoErr();
  779.                             ExitTransferThread(sd, ERROR_FILEOPEN);
  780.                             return;
  781.                         }
  782.                         sprintf(cmnt, "md5:%s; size:%ld", sd->song->md5, sd->size);
  783.                         SetComment(sd->fname, cmnt);
  784.                         Seek(sd->f, sd->cur, OFFSET_BEGINNING);
  785.                         sd->state = DLS_DOWN;
  786.                         sd->starttime = time(NULL);
  787.                         sd->resumestart = sd->cur;
  788.                         thr_message(t, DLC_UPDATE, 0);
  789.                     }
  790.  
  791.                 case DLS_DOWN:
  792.                     if (WaitSelect(s+1,&sd->fds,NULL,NULL,&sd->tv,0) != 1) break;
  793.                     while (sd->cur < sd->size) {
  794.  
  795.                         tmp = recv(s, buffer, 4096, 0);
  796.                         if (tmp < 1) {
  797.                             sd->ErrorCode = Errno();
  798.                             ExitTransferThread(sd, ERROR_NET);
  799.                             return;
  800.                         }
  801.                         SetIoErr(0L);    /* FWrite doesn't clear IoErr() due to a bug */
  802.                         count = FWrite(sd->f, buffer, 1, tmp);
  803.                         sd->cur += count;
  804.                         if (count != tmp) {
  805.                             sd->ErrorCode = IoErr();
  806.                             ExitTransferThread(sd, ERROR_FILEWRITE);
  807.                             return;
  808.                         }
  809.  
  810.                         m = (thmsg)GetMsg(t->port);
  811.                         if (m) {
  812.                             if (m->com == THC_EXIT) {
  813.                                 thr_message(t, DLC_STATE, (APTR)DLS_ABORT);
  814.                                 ExitTransferThread(sd, 0);
  815.                                 return;
  816.                             }
  817.                             if (!m->isreply) {
  818.                                 m->isreply = 1;
  819.                                 ReplyMsg((struct Message *)m);
  820.                             }
  821.                         }
  822.  
  823.                         FD_ZERO(&sd->fds);
  824.                         FD_SET(s,&sd->fds);
  825.                     }
  826.  
  827.                     sd->state = DLS_FIN;
  828.                     thr_message(t, DLC_UPDATE, 0);
  829.  
  830.                     if ((strlen(sd->song->user)+strlen(sd->host)+9) < 80) sprintf(cmnt, "from %s (@%s)", sd->song->user, sd->host);
  831.                     else sprintf(cmnt, "from %s (@%s)", sd->song->user, Inet_NtoA(sd->song->ip));
  832.                     /* If the hostname is too long for the file comment, we fall back to the IP */
  833.                     SetComment(sd->fname, cmnt);
  834.  
  835.                     if (prf->autoadd) thr_message(t, DLC_ADDSHARE, 0);
  836.  
  837.                     ExitTransferThread(sd, 0);
  838.                     return;
  839.  
  840.             }
  841.         }
  842.     }
  843.  
  844.     ExitTransferThread(sd, 0);
  845. }
  846.